<?php

/**
 * Celebrio Core
 * 
 * @copyright  Copyright (c) 2011 Celebrio Software
 * @package    Celebrio
 * @subpackage Core
 */

namespace Celebrio\Core;

use Nette\Object;

use \Microsoft_WindowsAzure_Storage_Table;
use \Microsoft_WindowsAzure_Storage_DynamicTableEntity;
use \Microsoft_Http_Client_Adapter_Exception;
use \Microsoft_WindowsAzure_Exception;

use Celebrio\Core\IConfigManagement;
use Celebrio\FileSystem\IOException;
use Celebrio\Core\ConfigManagementAzure;
use \Celebrio\Core\ConfigManagement;

use \InvalidArgumentException;

/**
 * Description of ConfigManagementAzure
 *
 * @author honza
 */
class ConfigManagementAzure extends Object implements IConfigManagement {
    
    const MAX_PROPERTY_SIZE = 65536;
    
    private $celebrioAzureConfigurationTableName = "celebrioconfigs";
    
    public function install($moduleName) {
        if(($this->isInstalled($moduleName))) {
            throw new ConfigException("Trying to install already installed module");
        }
        $configExists = $this->entityExists($moduleName);
        if(empty($configExists)) {
            $this->save(array(), $moduleName);
        }
        
        $entity = $this->entityExists('installed');
        if(empty($entity)) {
            $config = array($moduleName => "on");
            $this->save($config, 'installed');
        } else {
            $table = $this->getConnectionSettings();
            $values = $entity->getAzureProperty('common');
            $values .= ";" . $moduleName . "§" . "on";
            $entity->setAzureProperty('common', $values);
            try {
                $table->mergeEntity($this->celebrioAzureConfigurationTableName, $entity);
            } catch (Microsoft_Http_Client_Adapter_Exception $e) {
                throw new \IOException("Unable to connect", 0 ,$e);
            } catch (Microsoft_WindowsAzure_Exception $e) {
                throw new \IOException("Can not update file installed", 0, $e);
            }
        }
    }

    public function uninstall($moduleName) {
        
    }

    //TODO: refactoring of loading items from entity and using here in isInstalled
    public function isInstalled($moduleName) {
        $entity = $this->entityExists("installed");
        if(empty($entity))
            return false;
        $values = $entity->getAzureProperty('common');
        $result = $this->parseEntityValue($values);
        return (isset($result[$moduleName]) && $result[$moduleName] == ConfigManagement::ON);
    }

    public function loadConfigArray($name) {
        return $this->load($name);
    }

    public function saveConfigArray($array, $name) {
        $this->save($array, $name);
    }
    
    public function deleteConfigArray($name) {
        $this->delete($name);
    }
    
    private function delete($name) {
        $table = $this->getConnectionSettings();
        try {
            $entity = $this->entityExists($name);
            if(!empty( $entity )) {
                $table->deleteEntity($this->celebrioAzureConfigurationTableName, $entity);
            } else {
                throw new \InvalidArgumentException("This entity does not exists");
            }
        } catch (Microsoft_Http_Client_Adapter_Exception $e) {
            throw new \IOException("Unable to connect", 0 ,$e);
        } catch (Microsoft_WindowsAzure_Exception $e) {
            throw new \IOException("Can not delete this file", 0, $e);
        }
    }
    
    private function load($name) {
        $table = $this->getConnectionSettings();
        try {
            $entity = $table->retrieveEntityById($this->celebrioAzureConfigurationTableName, 'config', $name);
            $values = $entity->getAzureProperty("common");
            
            $result = $this->parseEntityValue($values);
        } catch (Microsoft_Http_Client_Adapter_Exception $e) {
            throw new IOException("Unable to connect", 0, $e);
        } catch (Microsoft_WindowsAzure_Exception $e) {
            throw new IOException("Could not load the entity", 0, $e);
        }
        
        return $result;
    }
    
    private function save($config, $name) {
        
        $table = $this->getConnectionSettings();
        $table->createTableIfNotExists($this->celebrioAzureConfigurationTableName);
        
        $entity = $this->getDynamicEntity("config", $name);
        
        $common = '';
        
        foreach ($config as $key => $value) {
	    if(is_array($value)){
		foreach ($value as $subkey => $subvalue){
		    $common .= $key.'.'.$subkey . '§' . $subvalue . ';';
		}
	    } else {
		$common .= $key . '§' . $value . ';';
	    }
        }
        
        $common = $this->validateConfigString($common);
        
        if(strlen($common) < self::MAX_PROPERTY_SIZE) {
            $entity->setAzureProperty('common', $common);
        } else {
            throw new InvalidArgumentException("Config is too large.", 0);
        }
        
        $exists = $this->entityExists($name);
        if(!empty($exists)) {
            throw new IOException("The entity already exists");
        }
        try {
            $table->insertEntity($this->celebrioAzureConfigurationTableName, $entity);
        } catch(Microsoft_Http_Client_Adapter_Exception $e) {
            throw new IOException("Unable to connect", 0, $e);
        } catch(Microsoft_WindowsAzure_Exception $e) {
            throw new IOException($e->getMessage(), 0, $e);
            //return null;
        }
    }
    
    public function test() {
        $table = $this->getConnectionSettings();
        //$entity = $table->retrieveEntityById($this->defaultCelebrioAzureConfigurationTableName, 'config', 'installed');
        //$entity = $table->retrieveEntities($this->defaultCelebrioAzureConfigurationTableName);
        //$entity = $table->retrieveEntities("testTable");
        //$this->entityExists("config-azure-test");
        //\Nette\Debug::dump($entity);
        //$this->entityExists("config-test-azure");
        
        
    }
    
    private function getConnectionSettings() {
	if(!extension_loaded("azure")) throw new \InvalidStateException("php_azure.dll is not loaded in PHP.");
        $storage = new Microsoft_WindowsAzure_Storage_Table(
                "table.core.windows.net", 
                azure_getconfig("StorageAccountName"), 
                azure_getconfig("StorageAccountKey")
	    );
        
        return $storage;
    }
    
    private function getDynamicEntity($name, $key) {
        $entity = new Microsoft_WindowsAzure_Storage_DynamicTableEntity($name, $key);
        return $entity;
    }
    
    private function entityExists($key) {
        $table = $this->getConnectionSettings();
        
        //This is unfinished try to make it better
        try {
            $entity = $table->retrieveEntityById($this->celebrioAzureConfigurationTableName, "config", $key);
        } catch(Microsoft_Http_Client_Adapter_Exception $e) {
            throw new IOException("Unable to connect", 0, $e);
        } catch(Microsoft_WindowsAzure_Exception $e) {
            return null;
        }
        return $entity;
    }
    
    private function validateConfigString($config) {
        if(substr($config, -1) === ";") {
            $config = substr($config, 0, strlen($config)-1);                
        }
        if(substr($config, 0, 1) === ";") {
            $config = substr($config, 1, strlen($config));
        }
        
        return $config;
    }
    
    private function parseEntityValue($values) {
        if(empty($values)) 
            return array();
        
        $values = \explode(";", $values);
            
        $result = array();
            
        foreach($values as $value) {
            $temp = explode("§", $value);
            if(strpos($temp[0], ".")){
            	$name = explode(".", $temp[0]);
            	if(isset($result[$name[0]])&&is_array($result[$name[0]])){
            		$result[$name[0]][$name[1]]=$temp[1];
            	} else {
            		$result[$name[0]] = array();
            		$result[$name[0]][$name[1]]=$temp[1];	
            	}
            } else {
            	$result[$temp[0]] = $temp[1];
            }
        }
        
        return $result;
    }

}
